package com.ruoyi.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.Firmware;
import com.ruoyi.system.domain.bo.CommitInfo;
import com.ruoyi.system.domain.bo.FileBo;
import com.ruoyi.system.domain.bo.FirmwareBo;
import com.ruoyi.system.domain.vo.FirmwareVo;
import com.ruoyi.system.mapper.DeviceMapper;
import com.ruoyi.system.mapper.FirmwareMapper;
import com.ruoyi.system.service.IFirmwareService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.FileUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
/**
 * 固件管理Service业务层处理
 *
 * @author whc
 * @date 2024-05-04
 */
@RequiredArgsConstructor
@Service
public class FirmwareServiceImpl implements IFirmwareService {

    private final FirmwareMapper firmwareMapper;
    private final DeviceMapper deviceMapper;
    private final String repoPath = System.getProperty("user.dir") + "/firmwares";

    @Override
    public List<CommitInfo> getFileCommits(String filePath) {
        List<CommitInfo> commitInfos = new ArrayList<>();

        try {
            Repository repository = Git.open(Paths.get(repoPath).toFile()).getRepository();
            Iterable<RevCommit> commits = getCommitsForFile(repository, filePath);
            for (RevCommit commit : commits) {
                CommitInfo commitInfo = new CommitInfo();
                commitInfo.setCommitId(commit.getName());
                commitInfo.setAuthor(commit.getAuthorIdent().getName());
                commitInfo.setDate(commit.getAuthorIdent().getWhen().toString());
                commitInfo.setMessage(commit.getShortMessage());

                // 获取文件的更改内容
                RevWalk revWalk = new RevWalk(repository);
                RevCommit parentCommit = null;
                if (commit.getParentCount() > 0) {
                    parentCommit = revWalk.parseCommit(commit.getParent(0));
                }

                String diff;
                if (parentCommit != null) {
                    diff = getDiffForFile(repository, parentCommit.getId(), commit.getId(), filePath);
                    commitInfo.setDiff(diff);
                }

                commitInfos.add(commitInfo);
            }
        } catch (IOException | GitAPIException e) {
            e.printStackTrace();
        }

        return commitInfos;
    }

    private String getDiffForFile(Repository repository, ObjectId oldCommitId, ObjectId newCommitId, String filePath) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            DiffFormatter diffFormatter = new DiffFormatter(outputStream);
            diffFormatter.setRepository(repository);
            diffFormatter.setDiffComparator(RawTextComparator.DEFAULT);
            diffFormatter.setDetectRenames(true);
            diffFormatter.format(oldCommitId, newCommitId);
        } finally {

        }

        return outputStream.toString(String.valueOf(StandardCharsets.UTF_8));
    }

    private Iterable<RevCommit> getCommitsForFile(Repository repository, String filePath) throws GitAPIException {
        try {
            Git git = new Git(repository);
            return git.log().addPath(filePath).call();
        } catch (GitAPIException e) {
            throw e;
        }
    }

    /**
     * 查询固件管理,同时返回deviceList以供选择
     */
    @Override
    public FirmwareVo queryById(Long id){
        return firmwareMapper.selectVoById(id);
    }
    @Override
    public boolean uploadFirmware(@RequestParam("file") FileBo fileBo) throws Exception {
        try {
            MultipartFile multipartFile = fileBo.getFile();
            Git git = new Git(new FileRepository("C:\\Users\\ddbbi\\Desktop\\毕设\\ruoyi-nbcio\\firmwares\\.git"));
            // 存放的路径
            Path path = Paths.get(repoPath, '\\' + fileBo.getPath());
            String commitMessage = "";
            if (!Files.exists(path)) {
                Files.createDirectories(path);
            }
            path = path.resolve(fileBo.getDeviceName() + ".bin");
            if (Files.exists(path)) {
                commitMessage = "Update ";
            } else {
                commitMessage = "Add ";
            }
            commitMessage += fileBo.getDeviceName() + ".bin:---Remark:" + fileBo.getRemark() + "---Version:" + fileBo.getVersion();
            multipartFile.transferTo(path);
            git.add().addFilepattern(fileBo.getPath()).call();
            Status status = git.status().call();
            Set<String> addedFiles = status.getAdded();
            git.commit().setMessage(commitMessage).call();
//            tempFile.transferTo(new File(path.toUri()));
        } catch (IOException e) {
            throw new Exception("文件上传失败，请重试");
        }
        return true;
    }

    @Override
    public ResponseEntity<FileSystemResource> getFile(String path) throws Exception {
        Path targetPath = Paths.get(repoPath, '/' + path);
        File file = new File(targetPath.toUri());
        if (!file.exists()) {
            return null;
        }
        FileSystemResource resource = new FileSystemResource(file);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", file.getName());
        return ResponseEntity.ok()
            .headers(headers)
            .body(resource);
    }

    @Override
    public ResponseEntity<FileSystemResource> getFile(String path, String commitId) throws Exception {
        try {
            Path targetPath = Paths.get(repoPath, '/' + path);
            File file = new File(targetPath.toUri());

            Repository repository = Git.open(new File(repoPath)).getRepository();
            try {
                RevWalk revWalk = new RevWalk(repository);
                ObjectId commitObjectId = repository.resolve(commitId);

                RevCommit commit = revWalk.parseCommit(commitObjectId);
                RevTree tree = commit.getTree();

                try {
                    TreeWalk treeWalk = new TreeWalk(repository);
                    treeWalk.addTree(tree);
                    treeWalk.setRecursive(true);
                    Path relativePath = repository.getWorkTree().toPath().relativize(targetPath);
                    treeWalk.setFilter(PathFilter.create(relativePath.getFileName().toString()));

                    ObjectId objectId = treeWalk.getObjectId(0);
                    if (objectId == null) {
                        throw new RuntimeException();
                    }
                    ObjectLoader loader = repository.open(objectId);
                    byte[] bytes = loader.getBytes();

                    // 将文件内容封装为 FileSystemResource 并返回
                    String tempFilePath = repoPath + "/temp.bin";
                    FileUtils.writeByteArrayToFile(new File(tempFilePath), bytes);
                    HttpHeaders headers = new HttpHeaders();
                    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
                    headers.setContentDispositionFormData("attachment", file.getName());
                    return ResponseEntity.ok()
                        .headers(headers)
                        .body(new FileSystemResource(tempFilePath));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                } catch (LargeObjectException e) {
                    throw new RuntimeException(e);
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (RuntimeException e) {
                throw new RuntimeException(e);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null; // 返回 null 或一个默认的 FileSystemResource，表示发生错误
        } finally {
            // 删除临时文件
//            deleteFile(repoPath + "/temp.bin");
        }
    }

    private void deleteFile(String filePath) {
        try {
            Path path = Paths.get(filePath);
            Files.deleteIfExists(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**L
     * 查询固件管理列表
     */
    @Override
    public TableDataInfo<FirmwareVo> queryPageList(FirmwareBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<Firmware> lqw = buildQueryWrapper(bo);
        Page<FirmwareVo> result = firmwareMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询固件管理列表
     */
    @Override
    public List<FirmwareVo> queryList(FirmwareBo bo) {
        LambdaQueryWrapper<Firmware> lqw = buildQueryWrapper(bo);
        return firmwareMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<Firmware> buildQueryWrapper(FirmwareBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<Firmware> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getVersion()), Firmware::getVersion, bo.getVersion());
        lqw.like(StringUtils.isNotBlank(bo.getDownloadLink()), Firmware::getDownloadLink, bo.getDownloadLink());
        lqw.eq(bo.getDeviceId() != null, Firmware::getDeviceId, bo.getDeviceId());
        return lqw;
    }

    /**
     * 新增固件管理
     */
    @Override
    public Boolean insertByBo(FirmwareBo bo) {
        Firmware add = BeanUtil.toBean(bo, Firmware.class);
        validEntityBeforeSave(add);
        boolean flag = firmwareMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }
//TODO 上传固件时，需要填版本号，版本号只能比之前的大
    //TODO 现在已经取消了版本号自增，填写时自己验证即可
    /**
     * 修改固件管理
     */
    @Override
    public Boolean updateByBo(FirmwareBo bo) {
        Firmware update = BeanUtil.toBean(bo, Firmware.class);
        validEntityBeforeSave(update);
        return firmwareMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(Firmware entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除固件管理
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return firmwareMapper.deleteBatchIds(ids) > 0;
    }
}
